package com.sneakxy.cloudbase.platform.handler;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import com.sneakxy.cloudbase.platform.utils.BindingResultUtils;
import com.sneakxy.cloudbase.platform.utils.web.Response;
import com.sneakxy.cloudbase.platform.utils.web.StringEscapeEditor;
import com.sneakxy.cloudbase.platform.utils.web.signature.SignatureErrorException;
import com.sneakxy.cloudbase.platform.utils.web.signature.SignatureTimeoutException;

import feign.FeignException;
import feign.RetryableException;

@ControllerAdvice
public class GlobalExceptionHandler {

	protected final Logger log = LoggerFactory.getLogger(getClass());
	
	public static final String DEFAULT_ERROR_MESSAGE = "功能服务未启动,请联系管理员.";
	
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		binder.registerCustomEditor(String.class, new StringEscapeEditor());
	}
	
	@ExceptionHandler(RetryableException.class)
	@ResponseBody
	public Response<Object> timeoutExceptionHandler(HttpServletRequest request, HttpServletResponse response, RetryableException exception) {
		log.error("服务未开启", exception);
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return Response.fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DEFAULT_ERROR_MESSAGE);
    }
	
	@ExceptionHandler(FeignException.class)
	@ResponseBody
	public Response<Object> feignExceptionHandler(HttpServletRequest request, HttpServletResponse response, FeignException exception) {
		log.error("服务未开启", exception);
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return Response.fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DEFAULT_ERROR_MESSAGE);
    }
	
	@ExceptionHandler(Exception.class)
	@ResponseBody
	public Response<Object> exceptionHandler(HttpServletRequest request, HttpServletResponse response, Exception exception) {
		log.error("未知异常", exception);
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return Response.fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, exception.getLocalizedMessage());
    }
	
	@ExceptionHandler(SignatureTimeoutException.class)
	@ResponseBody
	public Response<Object> signatureTimeoutExceptionHandler(HttpServletRequest request, HttpServletResponse response, SignatureTimeoutException exception) {
		log.error("签名超时", exception);
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return Response.fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "签名超时");
    }
	
	@ExceptionHandler(SignatureErrorException.class)
	@ResponseBody
	public Response<Object> signatureErrorExceptionHandler(HttpServletRequest request, HttpServletResponse response, SignatureErrorException exception) {
		log.error("签名错误", exception);
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return Response.fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "签名错误");
    }
		
	@ExceptionHandler(BindException.class)
	@ResponseBody
	public Response<Map<String, String>> bindExceptionHandler(HttpServletRequest request, HttpServletResponse response, BindException exception) {
		log.error("校验错误", exception);
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return BindingResultUtils.getFieldErrorsResponse(exception.getBindingResult());
    }
	
	@ExceptionHandler(MethodArgumentNotValidException.class)
	@ResponseBody
	public Response<Map<String, String>> bindExceptionHandler(HttpServletRequest request, HttpServletResponse response, MethodArgumentNotValidException exception) {
		log.error("校验错误", exception);
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return BindingResultUtils.getFieldErrorsResponse(exception.getBindingResult());
    }
	
	@ExceptionHandler(MissingServletRequestParameterException.class)
	@ResponseBody
	public Response<Object> signatureErrorExceptionHandler(HttpServletRequest request, HttpServletResponse response, MissingServletRequestParameterException exception) {
		log.error("参数错误", exception);
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return Response.fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, StringUtils.join("缺少参数[", exception.getParameterName(), "]"));
    }
	
	@ExceptionHandler(MaxUploadSizeExceededException.class)
	@ResponseBody
	public Response<Object> maxUploadSizeExceptionHandler(HttpServletRequest request, HttpServletResponse response, MaxUploadSizeExceededException exception) {
		log.warn("上传文件过大", exception);
		response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		return Response.fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "上传文件过大");
    }
	
	@ExceptionHandler(UnauthenticatedException.class)
	@ResponseBody
	public Response<Object> unauthExceptionHandler(HttpServletRequest request, HttpServletResponse response, UnauthenticatedException exception) {
		log.warn("用户未认证", exception);
		response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
		return Response.fail(HttpServletResponse.SC_UNAUTHORIZED, "用户未认证");
    }
	
	@ExceptionHandler(UnauthorizedException.class)
	@ResponseBody
	public Response<Object> unauthorizedExceptionHandler(HttpServletRequest request, HttpServletResponse response, UnauthorizedException exception) {
		log.warn("禁止访问功能", exception);
		response.setStatus(HttpServletResponse.SC_FORBIDDEN);
		return Response.fail(HttpServletResponse.SC_FORBIDDEN, "禁止访问功能");
    }
	
}
